import re
import geopandas as gpd
import pandas as pd
import numpy as np
from tqdm import tqdm
import matplotlib.pyplot as plt
import networkx as nx
import shapely
import copy,os,sys
from settings import *
# sys.path.append(os.path.dirname(os.getcwd()))

class Envelope:
    
    def __init__(self, xmin=0,ymin=0,xmax=0,ymax=0,crs_code=4326):
        self.xmin = xmin
        self.ymin = ymin
        self.xmax = xmax
        self.ymax = ymax
        self.crs_code = crs_code
        pass
    
    def ToString(self, sep = ","):
        return f"{self.xmin}{sep}{self.ymin}{sep}{self.xmax}{sep}{self.ymax}{sep}{self.crs_code}"
    

class Method_JoinMatchWell:
    def __init__(self,SRID = 32649):
        self.SRID = SRID
        pass
    
    def SetEnvironment():
        raise NotImplementedError
        pass
    
    def reverse(self,geom):
        pois = list(geom.coords)
        pois.reverse()
        # print(pois)
        return shapely.geometry.LineString(pois)

    def reverseRoad(self,df,reverseList,geomName = "geom"):
        geomIndex = list(df.columns).index(geomName)
        for i in reverseList:
            # print(i,geomIndex)
            geom = self.reverse(df.iloc[i,geomIndex])
            # print(df.iloc[i,geomIndex] )
            df.iloc[i,geomIndex] = geom
        return df

    def reverse_sort_Group(self,match_df,rp_df):
        g = nx.DiGraph()
        # df["reverse"] = np.zeros(len(df))
        nodes = []
        for i in range(len(match_df)):
            line = match_df.iloc[i].geometry
            pois = list(line.coords)
            nodes.append(pois[0])
            nodes.append(pois[-1])
            g.add_edge(pois[0],pois[-1],rid = i)
        nodes = list(set(nodes))
        degreeDf = pd.DataFrame(g.degree())
        degreeDf.columns = ["node","degree"]

        node_can = degreeDf[degreeDf.degree == 1]
        x0y0 = np.array(list(list(rp_df.geom)[0].coords)[0])
        xy_can =np.array(list(node_can.node),dtype =float)
        start_i = np.argmin(np.sum((xy_can - x0y0)**2,axis = 1))
        start = node_can.iloc[start_i,0]
        end = node_can.iloc[abs(start_i-1),0]
        reverseList = self.getReverseList(g,start)
        ## 获取顺序
        spath = nx.shortest_path(g,start,end)
        # rids = [list(g.edges(spath[i],spath[i+1],data = "rid"))[0][-1] for i in range(len(spath)-1)]
        edgeDf = pd.DataFrame(g.edges(data = "rid"))
        edgeDf.columns= ["start","end","rid"]
        rids = [edgeDf[(edgeDf.start == spath[i] ) & (edgeDf.end == spath[i+1])].iloc[0].rid for i in range(len(spath)-1)]
        # print(spath)
        # print(edgeDf)
        # print("edge：",rids)
        # nx.draw(g)

        match_df = self.reverseRoad(match_df,reverseList,"geometry")

        ## sort
        match_df = match_df.iloc[rids,:]
        # print("macth:",match_df)
        return match_df

    ## 桩号分配
    def distributeZH(self,match_df,rp_df):
        qdzh = float(rp_df.qdzh)
        zdzh = float(rp_df.zdzh)
        qdmc = rp_df.iloc[0].qdmc
        zdmc = rp_df.iloc[0].zdmc

        total_mile = float((zdzh-qdzh)*1000)
        lengths = np.array([match_df.iloc[i].geometry.length for i in range(len(match_df))])
        # print(lengths,total_mile)
        # distribute
        distributeMile = np.round(lengths*total_mile/np.sum(lengths)/1000,3)
        # print(lengths,total_mile,distributeMile)
        qdzhs = [qdzh] + list(distributeMile[0:-1]+qdzh)
        zdzhs =  qdzhs[1:] + [zdzh]
        qdmcs = [qdmc] + [f"插入点{i}" for i in range(len(match_df)-1)]
        zdmcs = [f"插入点{i}" for i in range(len(match_df)-1)] + [zdmc]
        # print(qdmcs,zdmcs)
        match_df["qdzh"] = qdzhs
        match_df["zdzh"] = zdzhs
        match_df["qdmc"] = qdmcs
        match_df["zdmc"] = zdmcs

    def getReverseList(self,g,start):
        edgesQuene = list(g.edges())
        edgeDf = pd.DataFrame(g.edges(data = "rid"))
        poi = start
        reverseList = []
        while(len(edgesQuene) != 0):
            edges = edgeDf[(edgeDf.iloc[:,0] == poi) | (edgeDf.iloc[:,1] == poi)]
            if(len(edges) == 0):
                break
            for edge_i in range(len(edges)):
                try:
                    edge = (edges.iloc[edge_i,0],edges.iloc[edge_i,1])
                    rid = edges.iloc[edge_i,2]
                    # print(edge)
                    index = edgesQuene.index(edge)
                    nodeIndex = edge.index(poi)
                    if(nodeIndex == 0):
                        pass
                    else:
                        # reverse
                        # print("reverse")
                        g.remove_edge(*edge)
                        edge_reverse = list(edge)
                        edge_reverse.reverse()
                        g.add_edge(*edge_reverse,rid = rid)
                        reverseList.append(rid)
                        pass
                    edgesQuene.remove(edge)
                    # print(len(edgesQuene))
                    poi = edge[abs(nodeIndex - 1)]
                    # print(f"next:{poi}")
                    break
                except ValueError:
                    pass
        return reverseList

    def MatchWellRoads_JoinWorkflow(self):
        pass     

    def joinMatchWellWorkflow(self,
                              ldbmList:list,
                              match_result,
                              rp_df,
                              result_shp_path:str,
                              SAVE_TAG:bool = False,
                              CRS:int = 32649):
        # # 设置环境
        # # match_result_c = gpd.read_file(r"H:\A1-University\Study\Graduate\P-RS_Road_match\data\match3\match3\match_lx_c.shp")
        # match_result_c = gpd.read_file(src_shp_path)
        # match_result_c["NID"] = [str(i) for i in range(len(match_result_c))]
        # rp_c_df = gpd.read_postgis(con = conn , sql = f"select * from lx_c where st_intersects(geom, ST_MakeEnvelope({Envelope.ToString()})")
        # match_result_c_well = match_result_c[match_result_c.BUFFER_SIM>THRESHOLD_BUFFER_SIM]
        # rp_c_ldbm_list = list(set(list(match_result_c_well.MATCH_ROAD)))

        # # 对所有的路段 进行合并测试
        # joinTag= []
        # length_simTag  = []
        # print("正在检查合并情况...")
        # for i,ldbm in tqdm(enumerate( rp_c_ldbm_list)):
        #     match_ld_part_df = match_result_c[match_result_c.MATCH_ROAD == ldbm]
        #     joinLine = shapely.ops.linemerge(list(match_ld_part_df.geometry))
        #     if type(joinLine) is shapely.geometry.linestring.LineString:
        #         joinTag.append(1)
        #     else:
        #         joinTag.append(0)

        #     length_simTag.append(joinLine.length / rp_c_df[rp_c_df.ldbm == ldbm].iloc[0].geom.length)

        #     # print(f"{i}->{joinTag[-1]}\t{length_simTag[-1]}")
        #     pass
        
        # tdf = pd.DataFrame([rp_c_ldbm_list,joinTag,length_simTag]).T
        # tdf.columns = ["ldbm","joinTag","lengthPer"]
        # x = tdf.joinTag == 1
        # y = tdf.lengthPer > 0.9
        # boolTag = np.logical_and(x,y)
        # ldbmList = tdf[boolTag].ldbm

        print("正在进桩号合并....")
        result_match_c = pd.DataFrame()
        for ldbm in tqdm(ldbmList):
            match_df = copy.deepcopy(match_result[match_result.match_road == ldbm])
            tmp_rp_df = rp_df[rp_df.ldbm_reset == ldbm]
            match_df = self.reverse_sort_Group(match_df,tmp_rp_df)
            self.distributeZH(match_df,tmp_rp_df)
            ldbm
            if(len(result_match_c) == 0):
                result_match_c = copy.deepcopy(match_df)
            else:
                result_match_c = result_match_c.append(copy.deepcopy(match_df))
            # print(len(result_match_c))
            pass
        
        # 存储
        if(SAVE_TAG):
            if(not os.path.exists("./result/join")):
                os.mkdir("./result/join")
            if(len(result_match_c) !=0 ):
                result_match_c.to_crs(f"EPSG:{CRS}").to_file(f"./result/join/{result_shp_path}",encoding = 'utf-8')

        # match_df_todo = match_result.query("NID not in @joinedNIDs")
        # print(f"总匹配数:{len(match_result)}\n融合完成个数:{len(result_match_c)}\n待匹配数量：{len(match_df_todo)}")
        # return [result_match_c,match_df_todo]


    # def matchWellWorkflow(self,src_shp_path:str,conn,Envelope:Envelope,result_shp_path:str,THRESHOLD_BUFFER_SIM:float = 0.8):
    #     # 设置环境
    #     # match_result_c = gpd.read_file(r"H:\A1-University\Study\Graduate\P-RS_Road_match\data\match3\match3\match_lx_c.shp")
    #     match_result_c = gpd.read_file(src_shp_path)
    #     match_result_c["NID"] = [str(i) for i in range(len(match_result_c))]
    #     rp_c_df = gpd.read_postgis(con = conn , sql = f"select * from lx_c where st_intersects(geom, ST_MakeEnvelope({Envelope.ToString()})")
    #     match_result_c_well = match_result_c[match_result_c.BUFFER_SIM>THRESHOLD_BUFFER_SIM]
    #     rp_c_ldbm_list = list(set(list(match_result_c_well.MATCH_ROAD)))

    #     # 对所有的路段 进行合并测试
    #     joinTag= []
    #     length_simTag  = []
    #     print("正在检查合并情况...")
    #     for i,ldbm in tqdm(enumerate( rp_c_ldbm_list)):
    #         match_ld_part_df = match_result_c[match_result_c.MATCH_ROAD == ldbm]
    #         joinLine = shapely.ops.linemerge(list(match_ld_part_df.geometry))
    #         if type(joinLine) is shapely.geometry.linestring.LineString:
    #             joinTag.append(1)
    #         else:
    #             joinTag.append(0)

    #         length_simTag.append(joinLine.length / rp_c_df[rp_c_df.ldbm == ldbm].iloc[0].geom.length)

    #         # print(f"{i}->{joinTag[-1]}\t{length_simTag[-1]}")
    #         pass
        
    #     tdf = pd.DataFrame([rp_c_ldbm_list,joinTag,length_simTag]).T
    #     tdf.columns = ["ldbm","joinTag","lengthPer"]
    #     x = tdf.joinTag == 1
    #     y = tdf.lengthPer > 0.9
    #     boolTag = np.logical_and(x,y)
    #     ldbmList = tdf[boolTag].ldbm

    #     print("正在进桩号合并....")
    #     result_match_c = pd.DataFrame()
    #     for ldbm in tqdm(ldbmList):
    #         match_df = copy.deepcopy(match_result_c[match_result_c.MATCH_ROAD == ldbm])
    #         rp_df = rp_c_df[rp_c_df.ldbm == ldbm]
    #         match_df = self.reverse_sort_Group(match_df,rp_df)
    #         self.distributeZH(match_df,rp_df)
    #         ldbm
    #         if(len(result_match_c) == 0):
    #             result_match_c = copy.deepcopy(match_df)
    #         else:
    #             result_match_c = result_match_c.append(copy.deepcopy(match_df))
    #         # print(len(result_match_c))
    #         pass
        
    #     # 存储
    #     if(not os.path.exists("../data/join")):
    #         os.mkdir("../data/join")
    #     result_match_c.to_file(f"../data/join/{result_shp_path}",encoding = 'utf-8')

    #     match_df_todo = match_result_c.query("NID not in @joinedNIDs")
    #     print(f"总匹配数:{len(match_result_c)}\n融合完成个数:{len(result_match_c)}\n待匹配数量：{len(match_df_todo)}")
    #     return [result_match_c,match_df_todo,tdf]

## 测试代码
if __name__ == "__main__":
    src_shp_path = r"H:\A1-University\Study\Graduate\P-RS_Road_match\data\match_reverse\match_lx_c.shp"
    envelope =Envelope( -55297,3959854,-17407,3993268,32649)
    result_shp_path ="join_match_well.shp"
    method = Method_JoinMatchWell()
    method.joinMatchWellWorkflow(src_shp_path,conn,envelope,result_shp_path)
    pass